<?php

declare(strict_types=1);

namespace request;

/**
 * API的基类
 */
class Request
{
	/** 
	 * @var string|null config目录中的配置文件名，类实例的配置会先自动从config目录中该文件获取
	 */
	public const CONFIG_NAME = null;

	/**
	 * @var array 配置项
	 */
	protected $config = [
		'store_data_dir' => '', // token储存功能目录
	];

	public function __construct($config = [])
	{
		if (static::CONFIG_NAME) {
			$this->config = array_merge($this->config, config(static::CONFIG_NAME));
		}
		$this->config = array_merge($this->config, $config);
	}

	/**
	 * 获取某个配置项
	 * @param mixed $name 配置项名称（string或者null，null获取全部，支持aa.bb嵌套获取）
	 * @param mixed $defaults 默认值
	 * @return mixed
	 */
	public function getConfig($name = null, $defaults = null)
	{
		if (is_null($name)) return $this->config;

		if (false === strpos($name, '.')) return $this->config[$name] ?? $defaults;

		$name    = explode('.', $name);
		$name[0] = strtolower($name[0]);
		$config  = $this->config;

		// 按.拆分成多维数组进行判断
		foreach ($name as $val) {
			if (isset($config[$val])) {
				$config = $config[$val];
			} else {
				return $defaults;
			}
		}

		return $config;
	}

	/**
	 * 储存一个本地数据（常用于token）
	 * @param string $key
	 * @param mixed $id
	 * @param mixed $data 如果传入非null的值，表示要写入数据
	 */
	public function storeData(string $key, $id = '', $data = null)
	{
		$dir = $this->getConfig('store_data_dir');
		if (!is_dir($dir)) mkdir($dir, 0777, true);

		$filename = $dir . $key . '-' . $id . '.json';

		if (!is_null($data)) {
			// 写入
			file_put_contents($filename, json_encode($data, JSON_UNESCAPED_UNICODE));
			return $data;
		} else {
			if (!file_exists($filename)) return false;
			return json_decode(file_get_contents($filename), true);
		}
	}

	/**
	 * curl发起Get请求
	 * @param string $url
	 * @param array $query 数据（自动拼接）
	 * @param array $headers 请求头
	 * @param array $curlOptions 其他要合并给curl_setopt的选项，例如 [CURLOPT_TIMEOUT => 10]
	 * @param mixed &$refCh 可以返回curl句柄
	 * @throws RequestException
	 */
	function curlGet($url = '', $query = [], $headers = [], $curlOptions = [], &$refCh = null)
	{
		// 初始化 cURL 会话
		$ch = curl_init();

		// 拼接查询参数到 URL
		if (!empty($query)) {
			$url = rtrim($url, '?&') . '?' . http_build_query($query);
		}

		// 设置 cURL 选项
		curl_setopt($ch, CURLOPT_URL, $url); // 设置请求 URL
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应结果，而不是直接输出
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // 设置请求头

		// 如果是 HTTPS 请求，则关闭 SSL 验证
		if (strpos($url, 'https://') === 0) {
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 关闭对等证书验证
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 关闭主机名验证
		}

		// 设置其他 cURL 选项
		foreach ($curlOptions as $option => $value) {
			curl_setopt($ch, $option, $value);
		}

		$refCh = $ch;

		// 执行 cURL 请求并获取响应
		$response = curl_exec($ch);

		// 关闭
		curl_close($ch);

		// 检查是否有错误发生
		if ($response === false) {
			throw new RequestException($ch);
		}

		// 处理响应体
		$responseContentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

		// 根据 Content-Type 处理响应体
		if (strpos($responseContentType, 'application/json') !== false) {
			$decodedResponse = json_decode($response, true); // 将 JSON 响应解码为数组形式
			if ($decodedResponse !== null) {
				$response = $decodedResponse;
			}
		}

		return $response;
	}

	/**
	 * 发起post请求
	 * @param string $url 网址，如需传参数可以使用 $url . '?' . http_build_query($query) 的方式
	 * @param string|array $body 请求参数，如果是string或二进制则作为请求体直接传输，如果是数组则根据Content-Type自动处理
	 * @param array $headers 请求头（如果传入空数组，Content-Type默认设为 application/x-www-form-urlencoded），请注意请求头的设置方法是 ['Content-Type: application/json']
	 * @param array $curlOptions 其他要合并给curl_setopt的选项，例如 [CURLOPT_TIMEOUT => 10]
	 * @param mixed &$refCh 可以返回curl句柄
	 * @throws RequestException
	 */
	function curlPost(string $url, $body, $headers = [], $curlOptions = [], &$refCh = null)
	{
		// 初始化 cURL 会话
		$ch = curl_init();

		// 设置 cURL 选项
		curl_setopt($ch, CURLOPT_URL, $url); // 设置请求 URL
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应结果，而不是直接输出
		curl_setopt($ch, CURLOPT_POST, true); // 设置为 POST 请求

		// 如果是 HTTPS 请求，则关闭 SSL 验证
		if (strpos($url, 'https://') === 0) {
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 关闭对等证书验证
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 关闭主机名验证
		}

		// 设置请求头
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

		// 设置请求体
		if (is_array($body)) {
			// 根据 Content-Type 处理请求体
			$content_type = '';
			foreach ($headers as $header) {
				if (strpos($header, 'Content-Type:') === 0) {
					$content_type = trim(str_replace('Content-Type:', '', $header));
					break;
				}
			}

			if (strpos($content_type, 'application/json') !== false) {
				$body = json_encode($body, JSON_UNESCAPED_UNICODE); // 转换为 JSON 字符串
			} else if (strpos($content_type, 'multipart/form-data') !== false) {
				$body = http_build_query($body); // 转换为 URL 编码的字符串
			} else if (strpos($content_type, 'application/x-www-form-urlencoded') !== false) {
				$body = http_build_query($body); // 转换为 URL 编码的字符串
			} else {
				$body = http_build_query($body); // 默认转换为 URL 编码的字符串
			}
		}
		curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

		// 设置其他 cURL 选项
		foreach ($curlOptions as $option => $value) {
			curl_setopt($ch, $option, $value);
		}

		$refCh = $ch;

		// 执行 cURL 请求并获取响应
		$response = curl_exec($ch);

		// 关闭
		curl_close($ch);

		// 检查是否有错误发生
		if ($response === false) {
			throw new RequestException($ch);
		}

		// 处理响应体
		$responseContentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

		// 根据 Content-Type 处理响应体
		if (strpos($responseContentType, 'application/json') !== false) {
			$decodedResponse = json_decode($response, true); // 将 JSON 响应解码为数组形式
			if ($decodedResponse !== null) {
				$response = $decodedResponse;
			}
		}

		return $response;
	}
}
